package com.wontube.core.utils;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;

import com.wontube.client.extension.utils.FileUtils;
import com.wontube.client.extension.utils.net.URLUtils;
import com.wontube.core.pub.AppEnvironment;



/**
 * @author yuechao
 */
public final class ClassUtils {
	private ClassUtils() {
	}

	/**
	 * 在指定类的路径下查找该类的非abstract子类
	 * 
	 * @param <T>
	 * 
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public static <T> ArrayList<Class<T>> loadSubClasses(Class<T> clazz) {
		ArrayList<Class<T>> list = new ArrayList<Class<T>>();

		String filename = URLUtils.getJarOrClassFile(clazz);
		// yuechao 解决有空格的问题，需要解码
		// @SuppressWarnings("deprecation")
		// File clazzFile = new File(URLDecoder.decode(filename));
		File clazzFile = new File(filename);
		String classesPath = AppEnvironment.getClassesRootPath();

		Package clazzPack = clazz.getPackage();
		String clazzPackName = clazzPack == null ? "" : clazzPack.getName();

		if (clazzFile.exists()) {
			if (clazzFile.getPath().toUpperCase().endsWith(".JAR")) { // clazz 在jar包中
				loadSubClassesFromJarFile(list, clazz, clazzFile.getAbsolutePath());

				clazzPackName = AppEnvironment.packageRoot
						+ clazzPackName.substring(AppEnvironment.corePackageRoot.length());
				String rootPath = FileUtils.combinePath(classesPath, clazzPackName.replace('.', File.separatorChar));

				// 在约定的子目录中搜索
				loadSubClassesFromFileSystem(list, clazz, rootPath, clazzPackName);
			} else { // calzz在文件系统中
				// 在相同目录子目录中搜索
				loadSubClassesFromFileSystem(list, clazz, clazzFile.getParent(), clazzPackName);

				if (clazzPackName.startsWith(AppEnvironment.corePackageRoot)) {
					clazzPackName = AppEnvironment.packageRoot
							+ clazzPackName.substring(AppEnvironment.corePackageRoot.length());
					String rootPath = FileUtils
							.combinePath(classesPath, clazzPackName.replace('.', File.separatorChar));

					loadSubClassesFromFileSystem(list, clazz, rootPath, clazzPackName);
				}
			}
		}

		return list;
	}

	private static <T> void loadSubClassesFromJarFile(ArrayList<Class<T>> list, Class<T> class0, String jarPath) {
		JarFile file;
		try {
			file = new JarFile(jarPath);
		} catch (IOException e1) {
			e1.printStackTrace();
			return;
		}

		Package pack = class0.getPackage();
		String packagePrefix = pack == null ? "" : pack.getName();

		Enumeration<JarEntry> entrys = file.entries();
		while (entrys.hasMoreElements()) {
			JarEntry entry = entrys.nextElement();
			String name = entry.getName();
			if (!name.endsWith(".class")) {
				continue;
			}

			if (name.indexOf('$') >= 0) {
				continue;
			}

			name = name.replace('/', '.').replace('\\', '.');

			if (!name.startsWith(packagePrefix)) {
				continue;
			}

			name = name.substring(0, name.length() - ".class".length());

			Class<T> cls = classForName(name);
			if (cls == null) {
				continue;
			}

			int m = cls.getModifiers();
			if (!(Modifier.isAbstract(m) || Modifier.isInterface(m)) && class0.isAssignableFrom(cls)) {
				list.add(cls);
			}
		}

		if (file != null) {
			try {
				file.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 在指定类的路径下查找该类的非abstract子类
	 * 
	 * @param <T>
	 * 
	 * @param clazz
	 * @return
	 */
	private static <T> void loadSubClassesFromFileSystem(ArrayList<Class<T>> list, Class<T> clazz, String rootPath,
			String rootPackName) {
		File rootFile = new File(rootPath);

		if (rootFile.exists()) {
			loadSubClassesFromFileSystem(list, clazz, rootPackName, rootFile, rootFile);
		}
	}

	/**
	 * 递归遍历指定目录并查找所有的子类
	 * 
	 * @param <T>
	 * 
	 * @param dir
	 * @param pack
	 * @return
	 */
	private static <T> void loadSubClassesFromFileSystem(ArrayList<Class<T>> list, Class<T> clazz, String rootPackName,
			File currentFile, File rootFile) {
		if (currentFile.isDirectory()) {
			for (File file : currentFile.listFiles()) {
				loadSubClassesFromFileSystem(list, clazz, rootPackName, file, rootFile);
			}
		} else {
			String filename = currentFile.getAbsolutePath();
			if (!filename.endsWith(".class")) {
				return;
			}

			if (filename.indexOf('$') >= 0) {
				return;
			}

			String relativeFilename = filename.substring(rootFile.getAbsolutePath().length(), filename.length()
					- ".class".length());

			String className = rootPackName + relativeFilename.replace('/', '.').replace('\\', '.');

			Class<T> cls = classForName(className);
			if (cls == null) {
				return;
			}

			int m = cls.getModifiers();
			if (!(Modifier.isAbstract(m) || Modifier.isInterface(m)) && clazz.isAssignableFrom(cls)) {
				list.add(cls);
			}
		}
	}

	@SuppressWarnings("unchecked")
	private final static <T> Class<T> classForName(String className) {
		try {
			Class<?> clazz = Class.forName(className);
			return (Class<T>) clazz;
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return null;
	}
}
